package com.ruoyi.web.utils;

import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.fastjson.JSON;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.poi.ExcelUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.MediaType;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;

/**
 * 导出工具
 *
 * @author sunfg
 * @date 2022-06-22
 */
public class EasyExcelUtils {

    private static final String CONTENT_TYPE = "application/vnd.ms-excel";
    private static final String CHARACTER_ENCODING = "utf-8";
    private static final String HEADER_S1 = "content-disposition";
    private static final String HEADER_S2 = "attachment; filename=";

    private final static Logger logger = LoggerFactory.getLogger(EasyExcelUtils.class);

    private EasyExcelUtils() {}

    /**
     * 创建excel，实现简单导出
     *
     * @param response 响应
     * @param templateName 导出使用的填充模板
     * @param fileName 导出的文件名称
     * @param map      表头非列表数据
     * @param list     列表数据
     * @throws Exception exception
     */
    public static void createExcel(HttpServletResponse response
            , String templateName, String fileName, Map<String, Object> map, List list) throws Exception {
        // 设置公共头信息
        OutputStream out = null;
        BufferedOutputStream bos = null;
        try {
            out = getOutputStream(fileName, response);
            bos = new BufferedOutputStream(out);

            // 获取泛型参数类型，指定类所在的目录来指定模板所在根路径
//            ParameterizedType type = (ParameterizedType) list.getClass()
//                    .getGenericSuperclass();
//            Type clazz = type.getActualTypeArguments()[0];
            Class<?> clazz = list.get(0).getClass();
            ExcelWriter excelWriter = EasyExcelFactory.write(bos)
                    .withTemplate(getResourcesFileInputStream(
                            templateName, clazz)).excelType(ExcelTypeEnum.XLS).build();
            WriteSheet writeSheet = EasyExcelFactory.writerSheet().build();
            FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
            // 填充 list 数据
            excelWriter.fill(list, fillConfig, writeSheet);
            // 填充 map 数据
            excelWriter.fill(map, writeSheet);
            excelWriter.finish();
            bos.flush();
        } catch (IOException e) {
            // 重置response
            response.reset();
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            response.setCharacterEncoding(CHARACTER_ENCODING);
            response.getWriter().println(JSON.toJSONString(AjaxResult.error("文件处理失败")));
            throw new Exception("导出excel表格失败!", e);
        } finally {
            if (bos != null)
            {
                try
                {
                    bos.close();
                }
                catch (IOException e1)
                {
                    e1.printStackTrace();
                }
            }
            if (out != null)
            {
                try
                {
                    out.close();
                }
                catch (IOException e1)
                {
                    e1.printStackTrace();
                }
            }
        }
    }

    /**
     * 导出excel，实现简单导出
     *
     * @param response 响应
     * @param templateName 导出使用的填充模板
     * @param fileName 导出的文件名称
     * @param map      表头非列表数据
     * @param list     列表数据
     * @throws Exception exception
     */
    public static AjaxResult exportExcel(HttpServletResponse response
            , String templateName, String fileName, Map<String, Object> map, List list) {
        // 设置公共头信息
        OutputStream out = null;
        try {
            out = new FileOutputStream(new ExcelUtil(null).getAbsoluteFile(fileName));

            Class<?> clazz = list.get(0).getClass();
            ExcelWriter excelWriter = EasyExcelFactory.write(out)
                    .withTemplate(getResourcesFileInputStream(
                            templateName, clazz)).excelType(ExcelTypeEnum.XLS).build();
            WriteSheet writeSheet = EasyExcelFactory.writerSheet().build();
            FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
            // 填充 list 数据
            excelWriter.fill(list, fillConfig, writeSheet);
            // 填充 map 数据
            excelWriter.fill(map, writeSheet);
            excelWriter.finish();
            return AjaxResult.success(fileName);
        } catch (IOException e) {
            logger.error("导出Excel异常{}", e.getMessage());
            throw new BaseException("导出Excel失败，请联系网站管理员！");
        } finally {
            if (out != null)
            {
                try
                {
                    out.close();
                }
                catch (IOException e1)
                {
                    e1.printStackTrace();
                }
            }
        }
    }

    /**
     * 导出文件时为Writer生成OutputStream
     *
     * @param fileName      导出文件名称
     * @param response      响应
     * @return  OutputStream
     */
    private static OutputStream getOutputStream(String fileName, HttpServletResponse response) throws IOException {
        fileName = URLEncoder.encode(fileName, CHARACTER_ENCODING);
        response.setContentType(CONTENT_TYPE);
        response.setCharacterEncoding(CHARACTER_ENCODING);
        response.setHeader(HEADER_S1, HEADER_S2 + fileName);
        response.setHeader("Pragma", "public");
        response.setHeader("Cache-Control", "no-store");
        response.addHeader("Cache-Control", "max-age=0");
        return response.getOutputStream();
    }

    /**
     * 获取报表模板路径
     *
     * @param templateName 文件名称
     * @param clazz        处理类
     */
    public static InputStream getResourcesFileInputStream(String templateName, Class<?> clazz) {
        return clazz.getClassLoader().getResourceAsStream("templates/excel/" + templateName);
    }

    /**
     * 获取报表模板路径
     *
     * @param templateName 文件名称
     * @param clazz        处理类
     */
    public static String getAbsoluteFile(String templateName, Class<?> clazz) throws IOException {
        String templatePath = new ClassPathResource(
                "templates/excel/" + templateName, clazz.getClassLoader()).getFile().getPath();
        File desc = new File(templatePath);
        if (!desc.getParentFile().exists())
        {
            desc.getParentFile().mkdirs();
        }
        return templatePath;
    }
}
